home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiniExamples / AppKit / WhatsUpDoc / Document.m < prev    next >
Text File  |  1995-06-12  |  8KB  |  345 lines

  1. /* Document.m
  2.  * Purpose:   Initializes, loads, archives and frees a single
  3.  *    document.  The template for the document is located in
  4.  *    Document.nib, which is loaded by this class each time
  5.  *    a new document is created.  The windows are tiled using
  6.  *    the newLocation() function.
  7.  *
  8.  * You may freely copy, distribute, and reuse the code in this example.
  9.  * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  10.  * fitness for any particular use.
  11.  *
  12.  * Written by: R. Dunbar Poor
  13.  * Created: 28/April/1991
  14.  *
  15.  */
  16. #import "Document.h"
  17. #import "DocController.h"
  18. #import <appkit/appkit.h>
  19. #import <objc/hashtable.h>    /* for NXCopyStringBufferFromZone */
  20. #import <sys/param.h>
  21. #import <strings.h>        /* for strncpy() */
  22. #import <stdlib.h>
  23.  
  24. @interface Document(DocumentPrivate)
  25. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain;
  26. - _write:(const char *)filename;
  27. - _read:(const char *)filename;
  28. @end
  29.  
  30. static void newLocation(NXPoint *p)
  31. /*
  32.  * This method computes a new location for each new window created.
  33.  */
  34. {
  35.   static count = 0;
  36.   p->x += (20.0 * count);
  37.   p->y -= (25.0 * count);
  38.   count = (count > 10)? 0 : count+1;
  39. }
  40.  
  41.  
  42. @implementation Document
  43.  
  44. - init
  45. /*
  46.  * The default initialization is simply to open a new (empty) document.
  47.  */
  48. {
  49.   return [self initFromFile:NULL];
  50. }
  51.  
  52. - initFromFile:(const char *)filename
  53. /*
  54.  * Read a document and initialize it from a file.
  55.  */
  56. {
  57.   [super init];
  58.   [NXApp loadNibSection:"Document.nib" owner:self withNames:NO];
  59.   [self loadFromFile:filename];
  60.   if (!filename) {
  61.     NXRect theFrame;
  62.     /* If its a new document, generate a new position for it */
  63.     [docWindow getFrame:&theFrame];
  64.     newLocation(&theFrame.origin);
  65.     [docWindow moveTo:theFrame.origin.x :theFrame.origin.y];
  66.   }
  67.   [docWindow makeKeyAndOrderFront:self];
  68.   /*
  69.    * I'm not sure why windowDidBecomeMain isn't called as a result of
  70.    * makeKeyAndOrderFront.  At any rate, we want new windows to become
  71.    * the "active" document.
  72.    */
  73.   [self windowDidBecomeMain:self];
  74.   return self;
  75. }
  76.  
  77. - loadFromFile:(const char *)filename
  78. /*
  79.  * Come here with all the nib objects instantiated.  We initialize any
  80.  * application-specific state from the contents from the given filename,
  81.  * and finish up any initialization.
  82.  */
  83. {
  84.   if (filename) {
  85.     if (![self _read:filename]) {
  86.       [self free];    /* couldn't load document file */
  87.       return nil;
  88.     }
  89.   }
  90.  
  91.   /*
  92.    * do some common setup.
  93.    */
  94.   [self setDocumentName:filename];
  95.   [self setDocEdited:NO];
  96.  
  97.   return self;
  98. }
  99.  
  100. - free
  101. {
  102.   /* tell the controller that we can no longer be the active doucment */
  103.   [[NXApp delegate] unsetActiveDocument:self];
  104.   if (name) free(name);
  105.   [docWindow free];
  106.   return [super free];
  107. }
  108.  
  109. - (const char *)message
  110. {
  111.   return [docContents stringValue];
  112. }
  113.  
  114. - activateDocument:sender
  115. {
  116.   [statusField setStringValue:"Active..."];
  117.   return self;
  118. }
  119.  
  120. - deactivateDocument:sender
  121. {
  122.   [statusField setStringValue:"Inactive..."];
  123.   return self;
  124. }
  125.  
  126. - hideDocument:sender
  127. {
  128.   [docWindow orderOut:sender];
  129.   return self;
  130. }
  131.  
  132. - setDocEdited:(BOOL)edited
  133. {
  134.   [docWindow setDocEdited:edited];
  135.   /*
  136.    * The following is a hack to tell the controller to update the
  137.    * Revert menu cell.  Unfortunately, it does lots of work besides
  138.    * that...
  139.    */
  140.   if ([[NXApp delegate] activeDocument] == self) {
  141.     [[NXApp delegate] setActiveDocument:self];
  142.   }
  143.   return self;
  144. }
  145.  
  146. - (BOOL)isDocEdited
  147. {
  148.   return [docWindow isDocEdited];
  149. }
  150.  
  151. - setDocumentName:(const char *)newName
  152. {
  153.   /*
  154.    * If we are passing 'name' itself as  the newName argument, as will happen
  155.    * in the revert: method, we don't want to modify it.  In particular, we
  156.    * better not call free() on it and then try to copy it back to itself (which
  157.    * was a bug I had for a while.)
  158.    */
  159.   if (newName != name) {
  160.     /* name isn't the same as newName, so it is safe to free name now */
  161.     if (name) free(name);
  162.     if (newName) {
  163.       name = NXCopyStringBuffer(newName);
  164.     } else {
  165.       name = NULL;
  166.     }
  167.   }
  168.  
  169.   if (name) {
  170.     [docWindow setTitleAsFilename:name];
  171.   } else {
  172.     [docWindow setTitleAsFilename:"Untitled Document"];
  173.   }
  174.  
  175.   return self;
  176. }
  177.  
  178. - save:sender
  179. {
  180.   if ([self isDocEdited] || !name) {
  181.     [self _saveWithNewName:NO retainNewName:YES];
  182.   }
  183.   return self;
  184. }
  185. - saveAs:sender {  return [self _saveWithNewName:YES retainNewName:YES]; }
  186. - saveTo:sender {  return [self _saveWithNewName:YES retainNewName:NO];  }
  187.  
  188. - revert:sender
  189. {
  190.   int choice;
  191.  
  192.   if ([self isDocEdited]) {
  193.     choice = NXRunAlertPanel(
  194.         "Revert",
  195.         "Discard changes to the document?",
  196.         "Revert",
  197.         "Cancel",
  198.         NULL);
  199.     switch (choice) {
  200.       case NX_ALERTDEFAULT:
  201.     [self loadFromFile:name];
  202.     break;
  203.       case NX_ALERTOTHER:
  204.     return nil;
  205.     }
  206.   }
  207.   return self;
  208. }
  209.  
  210. - close:sender
  211. {
  212.   [docWindow performClose:self];
  213.   return sender;
  214. }
  215.  
  216. - dirty:sender
  217. {
  218.   [self setDocEdited:YES];
  219.   return [self windowDidBecomeMain:self];
  220. }
  221.  
  222. - checkForEdited:sender
  223. /*
  224.  * If the document is edited, give the user a chance to save the
  225.  * document.  Returns nil if they want to cancel.
  226.  */
  227. {
  228.   int choice;
  229.  
  230.   if ([self isDocEdited]) {
  231.     [docWindow makeKeyAndOrderFront:self];
  232.     choice = NXRunAlertPanel(
  233.         "Close",
  234.         "Save changes to %s?",
  235.         "Save",        /* NX_ALERTDEFAULT */
  236.         "Don't Save",    /* NX_ALERTALTERNATE */
  237.         "Cancel",    /* NX_ALERTOTHER */
  238.         (name)?name:"Untitled Document");
  239.     switch (choice) {
  240.       case NX_ALERTALTERNATE:
  241.     break;
  242.       case NX_ALERTDEFAULT:
  243.     [self save:nil];
  244.     break;
  245.       case NX_ALERTOTHER:
  246.     return nil;
  247.     }
  248.   }
  249.   return self;
  250. }
  251.  
  252. - windowDidBecomeMain:sender
  253. {
  254.   [[NXApp delegate] setActiveDocument:self];
  255.   return [self activateDocument:sender];
  256. }
  257.  
  258. - windowDidResignMain:sender
  259. {
  260.   return [self deactivateDocument:sender];
  261. }
  262.  
  263. - windowWillClose:sender
  264. {
  265.   if (![self checkForEdited:sender]) return nil;
  266.  
  267.   /* make the document disavow any knowledge of the window */
  268.   [docWindow setDelegate:nil];
  269.   docWindow = nil;
  270.  
  271.   /* The document can't live now that its window is gone... */
  272.   [self free];
  273.   return sender;
  274. }
  275.  
  276. /*
  277.  * All varieties of save go through this routine.  It covers all the cases
  278.  * of running the Save Panel and retaining the name chosen.
  279.  */
  280. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain
  281. {
  282.   id savePanel;
  283.   const char *saveName;        /* filename to save into */
  284.  
  285.   if (!name || doNewName) {
  286.     /* saveAs or saveTo */
  287.     savePanel = [SavePanel new];
  288.     [savePanel setRequiredFileType:DOCUMENT_TYPE];
  289.     if ([savePanel runModalForDirectory:NULL file:name]) {
  290.       saveName = [savePanel filename];
  291.     } else {
  292.       /* aborted out? */
  293.       return self;
  294.     }
  295.   } else {
  296.     /* ordinary Save */
  297.     saveName = name;
  298.   }
  299.   [self _write:saveName];
  300.  
  301.   /* update the document name if requested */
  302.   if (doRetain) {
  303.     [self setDocumentName:saveName];
  304.     [docWindow setDocEdited:NO];
  305.   }
  306.   return self;
  307. }
  308.  
  309. - _write:(const char *)filename
  310. {
  311.   NXTypedStream *ts;
  312.   NXRect theFrame;
  313.   const char *contents;
  314.  
  315.   ts = NXOpenTypedStreamForFile(filename, NX_WRITEONLY);
  316.   if (!ts) return self;
  317.   [docWindow getFrame:&theFrame];
  318.   NXWriteRect(ts, &theFrame);
  319.   contents = [docContents stringValue];
  320.   NXWriteType(ts, "*", &contents);
  321.   NXCloseTypedStream(ts);
  322.  
  323.   return self;
  324. }
  325.  
  326. - _read:(const char *)filename
  327. {
  328.   NXTypedStream *ts;
  329.   NXRect theFrame;
  330.   char *contents;
  331.  
  332.   ts = NXOpenTypedStreamForFile(filename, NX_READONLY);
  333.   if (!ts)
  334.     return self;
  335.   NXReadRect(ts, &theFrame);
  336.   [docWindow placeWindowAndDisplay:&theFrame];
  337.   NXReadType(ts, "*", &contents);
  338.   [docContents setStringValue:contents];
  339.   NXCloseTypedStream(ts);
  340.  
  341.   return self;
  342. }
  343.  
  344. @end
  345.